package com.glodon.paas.document.web.controller;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.glodon.paas.consts.StringConst;
import com.glodon.paas.document.api.bean.File;
import com.glodon.paas.document.api.bean.PublicToken;
import com.glodon.paas.document.service.FileService;
import com.glodon.paas.document.service.ShareService;
import com.glodon.paas.document.web.util.SecurityUtils;
import com.glodon.paas.exception.PaasServerException;
import com.glodon.paas.exception.PaasServerExceptionFactory;
import com.glodon.paas.storage.ReadResult;
import com.glodon.paas.util.FileUtil;

@Controller
@RequestMapping("/public")
public class PublicController {

    @Autowired
    private ShareService shareService;

    @Autowired
    private FileService  fileService;

    @RequestMapping(value = "/file/{token}", method = RequestMethod.GET)
    public ModelAndView publicPage(@PathVariable("token")
    String token, HttpServletRequest request, ModelMap model) {
        PublicToken pToken = shareService.getPublicToken(token);
        if (pToken != null) {
            File file = fileService.getFile(StringConst.ANONYMOUS_USERID, pToken.getFileId(), false);
            file.getAttributes().put(StringConst.DATABAG_TOKEN, pToken.getDatabagToken());
            model.addAttribute(StringConst.FILE, file);
            model.addAttribute(StringConst.USER, SecurityUtils.getSessionUser(request));
        } else {
            throw PaasServerExceptionFactory.accessForbidden("Token[value:" + token + "] expired.", null);
        }
        return new ModelAndView("public");
    }

    @RequestMapping(value = "/file/download/{fileId}", method = RequestMethod.GET)
    public void downloadFile(@PathVariable("fileId")
    String fileId, HttpServletRequest request, HttpServletResponse response, ModelMap model) {
        File file = fileService.getFile(StringConst.ANONYMOUS_USERID, fileId, false);
        if (file.isFolder()) {
            downloadFolder(file, request, response, model);
        } else {
            downloadFile(file, request, response);
        }
    }

    private void downloadFile(File file, HttpServletRequest request, HttpServletResponse response) {
        // check if the request contains Range header, and extract the range
        // values if existing
        long start = -1, end = -1;
        String rangeHeader = request.getHeader("Range");
        boolean doRange = false;
        if (rangeHeader != null) {
            String startStr = StringUtils.substringBetween(rangeHeader, "bytes=", "-");
            String endStr = StringUtils.substringAfter(rangeHeader, "-");
            if (StringUtils.isNumeric(startStr) && StringUtils.isNumeric(endStr)) {
                start = Long.parseLong(startStr);
                if (StringUtils.isNotBlank(endStr)) {
                    end = Long.parseLong(endStr);
                }
                doRange = true;
            }
        }
        InputStream is = null;
        OutputStream os = null;
        try {
            ReadResult streamResponse = null;
            response.reset();
            response.setCharacterEncoding(StringConst.UTF8);
            if (doRange) {
                streamResponse = fileService.downloadFile(StringConst.ANONYMOUS_USERID, file.getId(), -1, start, end);
                response.setHeader("Accept-Ranges", "bytes");
                response.setHeader("Content-Range",
                                   "bytes " + streamResponse.getRanges()[0] + "-" + streamResponse.getRanges()[1] + "/"
                                           + streamResponse.getRanges()[2]);
                response.setStatus(206);
            } else {
                streamResponse = fileService.downloadFile(StringConst.ANONYMOUS_USERID, file.getId(), -1);
            }

            is = streamResponse.getInputStream();
            os = response.getOutputStream();
            response.addHeader("Content-Disposition",
                               "attachment; filename=\"" + FileUtil.encodeFileName(request, streamResponse.getName())
                                       + "\"");
            response.addHeader("Content-Length", Long.toString(streamResponse.getLength()));
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            IOUtils.copy(is, os);
        } catch (PaasServerException de) {
            throw de;
        } catch (Exception er) {
            throw PaasServerExceptionFactory.systemException(er.getMessage(), er);
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
        }
    }

    private void downloadFolder(File file, HttpServletRequest request, HttpServletResponse response, ModelMap model) {
        response.addHeader("Content-Disposition",
                           "attachment; filename=\"" + FileUtil.encodeFileName(request, file.getName() + ".zip") + "\"");
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        try {
            fileService.downloadFolder(StringConst.ANONYMOUS_USERID, file.getId(), response.getOutputStream());
        } catch (IOException e) {
            throw PaasServerExceptionFactory.systemException(e.getMessage(), e);
        }
    }
}
